<?php

declare(strict_types=1);

namespace App\Utils\Encrypt;

use App\Master\Enum\RedisKeyEnum;
use App\Utils\RedisUtil;
use Hyperf\Config\Annotation\Value;
use Hyperf\Coroutine\Coroutine;

class Token
{
    /**
     * Token 配置信息
     * @var array
     */
    #[Value("token.default")]
    protected array $config;
    private static $instances = [];

    /**
     * 协程单例模式处理
     * @return mixed|self
     */
    public static function getInstance()
    {
        $instanceName = Coroutine::id() . uniqid();//协程id&唯一id组成
        if (!isset(self::$instances[$instanceName])) {
            self::$instances[$instanceName] = new self();
            Coroutine::defer(function () use ($instanceName) {
                unset(self::$instances[$instanceName]);
            });
        }
        return self::$instances[$instanceName];
    }

    /**
     * 创建token
     * @param array $data
     * @return string
     * @throws \Exception
     */
    public function create(array $data): string
    {
        $data = array_merge($data, [
            'iat' => time(),// 签发时间
            'exp' => time() + $this->config['expire'],// 过期时间
        ]);
        $dataJson = json_encode($data, JSON_UNESCAPED_UNICODE);
        $token = Aes::encode($dataJson, $this->config['key']);

        // 校验唯一token，单点登录
        if ($this->config['sso'] === true) {
            $token_key = (string)key($data);
            $token_id = (string)reset($data);
            RedisUtil::getInstance(RedisKeyEnum::TOKEN_ONCE, $token_key, $token_id)->setex($token, $this->config['expire']);
        }

        return $token;
    }

    /**
     * 验证token是否有效
     * @param string $token
     * @return false|mixed
     * @throws \Exception
     */
    public function check(string $token)
    {
        //验证token
        if (!$data = Aes::decode($token, $this->config['key'])) {
            return false;
        }
        $data = json_decode($data, true);

        // 校验唯一token，单点登录
        if ($this->config['sso'] === true) {
            $token_key = (string)key($data);
            $token_id = (string)reset($data);
            if (!$userToken = RedisUtil::getInstance(RedisKeyEnum::TOKEN_ONCE, $token_key, $token_id)->get()) {
                return false;
            }
            if ($userToken != $token) {
                return false;
            }
        }

        return $data;
    }

    /**
     * 删除token
     * 仅开启单点登录时 可用
     * @param array $data 生成token时传递的参数
     * @return bool
     * @throws \Exception
     */
    public function remove(array $data)
    {
        // 单点登录 删除登录状态
        if ($this->config['sso'] === true) {
            $token_key = (string)key($data);
            $token_id = (string)reset($data);
            //验证token
            if (!RedisUtil::getInstance(RedisKeyEnum::TOKEN_ONCE, $token_key, $token_id)->del()) {
                return false;
            }
        }
        return true;
    }
}